package com.dc.pay.callback.template;

import com.dc.pay.constant.PayConstant;
import com.dc.pay.mapper.PaymentTransactionLogMapper;
import com.dc.pay.mapper.entity.PaymentTransactionLogEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @author: KEYD
 * @Date: 2020/1/13 14:54
 * @Desc: 使用模板方法重构异步回调代码
 */
@Slf4j
@Component
public abstract class AbstractPayCallbackTemplate {

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;  //封装多线程

    @Autowired
    private PaymentTransactionLogMapper paymentTransactionLogMapper;


    /**
     * 获取所有的请求参数，存放到map集合，并且进行参数的验证
     *
     * @param req
     * @param resp
     * @return
     */
    public abstract Map<String, String> verifySignature(HttpServletRequest req, HttpServletResponse resp);

    /**
     * 重试
     *
     * @return
     */
    public abstract String failResult();

    public abstract String successResult();

    /**
     * 异步回调执行业务逻辑
     *
     * @param verifySignature
     * @return
     */
    @Transactional
    protected abstract String asyncService(Map<String, String> verifySignature);

    /**
     * 1、将报文数据存放到 es。
     * 2、验证报文参数
     * 3、将日志根据支付ID存放到数据库中
     * 4、执行异步回调业务逻辑。
     */

    //异步回调的方法
    @Transactional
    public String asyncCallBack(HttpServletRequest req, HttpServletResponse resp) {
        //***将请求日志写入到ES中。
        //1、验证报文参数.相同点：获取所有的请求参数封装成map集合,并且进行参数验证
        Map<String, String> verifySignature = verifySignature(req, resp);
        //2、将日志根据支付id存放到数据库中
        String paymentId = verifySignature.get("paymentId");
        if (StringUtils.isEmpty(paymentId)) {
            return failResult();
        }

        //3、采用异步形式写入日志到数据库中。
        //payLog(paymentId, verifySignature);
        threadPoolTaskExecutor.execute(new PayLogThread(paymentId, verifySignature));
        //报文验证签名失败
        String result = verifySignature.get(PayConstant.RESULT_NAME);
        if (result.equals(PayConstant.RESULT_PAYCODE_201)) {
            return failResult();
        }
        //3、执行的异步回调业务逻辑
        String asyncService = asyncService(verifySignature);
        return asyncService;
    }


    /**
     * 采用多线程或者MQ形式存放到数据库中
     * MQ 会自动补偿
     * 使用多线程写入日志的目的：加快响应，提高程序效率
     * 线程池维护线程
     * 采用多线程技术或者MQ形式进行存放到数据库中
     *
     * @param paymentId
     * @param verifySignature
     */
    private void payLog(String paymentId, Map<String, String> verifySignature) {
        log.info(">>paymentId:{paymentId},verifySignature:{}", verifySignature);
        PaymentTransactionLogEntity paymentTransactionLogEntity = new PaymentTransactionLogEntity();
        paymentTransactionLogEntity.setAsyncLog(verifySignature.toString());
        paymentTransactionLogEntity.setTransactionId(paymentId);
        paymentTransactionLogMapper.insertTransactionLog(paymentTransactionLogEntity);
    }

    // A 1423 B 1234

    /**
     * 使用多线程写入日志目的：加快响应 提高程序效率 使用线程池维护线程
     */
    class PayLogThread implements Runnable {
        private String paymentId;
        private Map<String, String> verifySignature;

        public PayLogThread(String paymentId, Map<String, String> verifySignature) {
            this.paymentId = paymentId;
            this.verifySignature = verifySignature;
        }

        @Override
        public void run() {
            log.info(">>>>>asyncCallBack service 02");
            payLog(paymentId, verifySignature);
            log.info(">>>>>asyncCallBack service 03");
        }

    }


}














